#pragma once

#include <gtl/type/inner.h>

//! \brief enumeration to specify the byte ordering of a sequence.
enum Endianess
{
	//! \brief specifies little endian order,
	//!        the least significant byte comes first.
	littleEndian,
	//! \brief specifies big endian order,
	//!        the most significant byte comes first.
	bigEndian
};

//! \brief This namespace is used to hold machine dependent definitions for
//!        the target machine.
namespace Machine
{
	//! \brief Specifies the natural byte ordering of the target machine.
	//! \todo detect endianess during compilation.
	const Endianess endianess = littleEndian; // x86
#ifndef _WIN64
	inline uint64 cpuFlags()
	{
		__asm
		{
			mov   eax, 1
				cpuid
				mov   eax, edx
				mov   edx, ecx
		}
	}

	inline bool SupportsMMX() { return ( cpuFlags() & 0x00800000 ) != 0; }
	inline bool SupportsSSE() { return ( cpuFlags() & 0x02000000 ) != 0; }
	inline bool SupportsSSE2() { return ( cpuFlags() & 0x04000000 ) != 0; }
	inline bool SupportsSSE3() { return ( cpuFlags() & 0x100000000 ) != 0; }
#elif defined _WIN64
	inline bool SupportsMMX() { return false; }
	inline bool SupportsSSE() { return true; }
	inline bool SupportsSSE2() { return true; }
#endif
}

//! \brief generic function to swap the byte ordering of a given type
//!
//! The byte ordering can be swapped meaningfully only for unsigned integer types
//! therefore specializations are provided only for those types. We use
//! template specialization in order to avoid automatic argument conversion.
template<typename T>
struct SwapEndianess {};

template<> struct SwapEndianess< uint8 >
{
	uint8 operator()(uint8 value) const { return value; }
};
template<> struct SwapEndianess< uint16 >
{
	uint16 operator()(uint16 value) const
	{
		return _byteswap_ushort( value );
	}
};

template<> struct SwapEndianess< uint32 >
{
	uint32 operator()(uint32 value) const
	{
		return _byteswap_ulong( value );
	}
};

template<> struct SwapEndianess< uint64 >
{
	uint64 operator()(uint64 value) const
	{
		return _byteswap_uint64( value );
	}
};

template<typename T>
inline T swapEndianess(T value)
{
	return SwapEndianess< T >()( value );
}

template<typename T, Endianess endianPolicy> struct TransformTo
{
	T operator()(T value) const { return swapEndianess< T >( value ); }
};

template<typename T > struct TransformTo< T, Machine::endianess >
{
	T operator()(T value) const { return value; }
};

//! \brief Generic function to bring a given value into little endian order.
template<typename T> inline T transformToLE(T value)
{
	return TransformTo< T, littleEndian >()( value );
}

//! \brief Generic function to bring a given value into big endian order.
template<typename T> inline T transformToBE(T value)
{
	return TransformTo< T, bigEndian >()( value );
}

//! \brief Generic function object to reconstruct a value out of its serialized
//!        form with a specified byte ordering.
//!
//! This function objects behaves the same as TransformTo does but its purpose
//! is different. Having both functions allows to make that purpose explicit in
//! code.
template<typename T, Endianess endianPolicy> struct TransformFrom
{
	T operator()(T value) const {	return TransformTo< T, endianPolicy >()( value ); }
};

//! \brief Generic function to reconstruct a given value from little endian
//!        order.
template<typename T> inline T transformFromLE(T value)
{
	return TransformFrom< T, littleEndian >()( value );
}

//! \brief Generic function to reconstruct a given value from big endian
//!        order.
template<typename T> inline T transformFromBE(T value)
{
	return TransformFrom< T, bigEndian >()( value );
}

//! Helper function to rotate the bits of a given unsigned value.
template<typename T> inline T rotateLeft(T value, uint8 shift);
template<> inline uint8 rotateLeft(uint8 value, uint8 shift)
{
	return uint8( value << shift | value >> ( 8 - shift ) );
}
template<> inline uint16 rotateLeft(uint16 value, uint8 shift)
{
	return uint16( value << shift | value >> ( 16 - shift ) );
}
template<> inline uint32 rotateLeft(uint32 value, uint8 shift)
{
	return uint32( value << shift | value >> ( 32 - shift ) );
}
template<> inline uint64 rotateLeft(uint64 value, uint8 shift)
{
	return uint64( value << shift | value >> ( 64 - shift ) );
}
